package com.uraurora.crescent.function.monad;

import java.util.Collection;
import java.util.Objects;
import java.util.concurrent.Callable;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;

/**
 * @author : gaoxiaodong04
 * @program : crescent
 * @date : 2020-10-14 10:52
 * @description :用来模拟函数式编程中的高阶类型
 */
public interface IKind<F, A> {

    F getF();

    A getA();

    void setA(A a);

    default <B> IKind<F, B> pure(B b){
        return Kind.of(getF(), b);
    }

    default <B> IKind<F, B> flatMap(Function<? super A, ? extends IKind<F, B>> f){
        return f.apply(getA());
    }

    default <B> IKind<F, B> map(Function<? super A, ? extends B> f){
        return flatMap(a->pure(f.apply(a)));
    }

    default IKind<F, A> apply(Consumer<? super A> action){
        with(getA(), action);
        return this;
    }

    default A getOrDefault(A defaultValue){
        return orDefault(getA(), defaultValue);
    }

    default A getOrElse(Callable<? extends A> callable) throws Exception {
        return orElse(getA(), callable);
    }

    default void let(Consumer<? super A> action){
        let(getA(), action);
    }

    default IKind<F, A> takeIf(Predicate<? super A> condition){
        setA(with(getA(), condition, o->{}));
        return this;
    }

    default IKind<F, A> takeUnless(Predicate<? super A> condition){
        // todo:
        return this;
    }

    default IKind<F, A> ifNull(Consumer<? super A> action){
        // todo:
        return this;
    }

    //<editor-fold desc="高阶函数">
    /**
     * 功能类似kotlin中的let，接受一个T类型的接受者，如果其为null则返回null
     * 不为空则返回func函数执行后的结果
     * @param object 接受者类型
     * @param func 函数
     * @param <T> 接受者类型
     * @param <R> 返回类型
     * @return 处理后的结果
     */
    public static  <T, R> R map(T object, Function<? super T, ? extends R> func){
        return object != null ? func.apply(object) : null;
    }

    /**
     * 功能类似kotlin中的let，接受一个T类型的接受者，如果其为null则不作任何操作
     * 不为空则执行func函数
     * @param object 接受者对象
     * @param func 函数
     * @param <T> 接受者类型
     */
    public static  <T> void let(T object, Consumer<? super T> func){
        if (object != null) {
            func.accept(object);
        }
    }

    public static  <T> void letCollection(Collection<T> collection, Consumer<? super Collection<T>> func){
        if (collection != null && collection.size()>0) {
            func.accept(collection);
        }
    }

    /**
     * 功能类似kotlin中的with，接受一个T类型的接受者，如果其为null则返回null
     * 不为空则对其引用进行操作，之后返回其引用
     * @param object 接收者对象
     * @param func 处理函数
     * @param <T> 接收者类型
     * @return 处理过的接收者对象引用
     */
    public static  <T> T with(T object, Consumer<? super T> func){
        if (object != null) {
            func.accept(object);
            return object;
        } else {
            return null;
        }
    }

    /**
     * 功能类似kotlin中的with，包含了一个 {@link Predicate} 的条件，接受一个T类型的接受者，
     * 如果其不为null且满足条件则对其引用进行操作，之后返回其引用，
     * 为空则返回空
     * @param object
     * @param condition
     * @param func
     * @param <T>
     * @return
     */
    public static  <T> T with(T object, Predicate<? super T> condition, Consumer<? super T> func){
        if (object != null && condition.test(object)) {
            func.accept(object);
            return object;
        } else {
            return null;
        }
    }
    //</editor-fold>

    //<editor-fold desc="非空对象默认值">
    /**
     * 功能类似kotlin中的?:操作符，用在最后包装对象
     * 如果对象为null则返回指定的默认值other，如果不为空则返回自身
     * @param object 接收者对象
     * @param other 默认值
     * @param <T> 接收者类型
     * @return 对象自身或默认值
     */
    public static  <T> T orDefault(T object, T other){
        return object != null ? object : other;
    }

    /**
     * 功能类似kotlin中的简化版的if else表达式，用在最后包装对象
     * 如果对象不为空且满足条件则返回自身，否则返回指定的默认值other
     * @param object 接收者对象
     * @param condition 条件
     * @param other 默认值
     * @param <T> 接收者类型
     * @return 对象自身或默认值
     */
    public static  <T> T orDefault(T object, Predicate<? super T> condition, T other){
        return object != null && condition.test(object) ? object : other;
    }

    public static  <T> T orElse(T object, Callable<? extends T> callable) throws Exception {
        return orDefault(object, callable.call());
    }

    public static  <T> T orElse(T object, Predicate<? super T> condition, Callable<T> callable) throws Exception {
        return orDefault(object, condition, callable.call());
    }
    //</editor-fold>

    /**
     * 相当于kotlin中的!!，不允许为null，为null会抛出异常
     * @param object 接收者对象
     * @param <T> 接收者类型
     * @return 接收者对象引用
     * @throws NullPointerException 空指针异常
     */
    public static <T> T notNull(T object) throws NullPointerException{
        return Objects.requireNonNull(object);
    }

    //<editor-fold desc="非空类型转换">

    /**
     * 类型转换逻辑，如果无法转换则返回null
     * @param obj 对象
     * @param clazz 类型
     * @param <T> 类型参数
     * @return 转化后的对象或者null
     */
    @SuppressWarnings("unchecked")
    public static <T> T cast(Object obj, Class<T> clazz){
        if (obj != null && !clazz.isInstance(obj)) {
            return null;
        } else{
            return (T) obj;
        }
    }

    /**
     *
     * @param obj 对象
     * @param clazz 类型
     * @param defaultValue 默认值
     * @param <T> 类型参数
     * @return 转化后的对象或者默认值
     */
    public static <T> T cast(Object obj, Class<T> clazz, T defaultValue){
        return orDefault(cast(obj, clazz), defaultValue);
    }
    //</editor-fold>

}
